<?php
/**
 * Created by PhpStorm.
 * User: Qch
 * Date: 2016/8/15
 * Time: 20:11
 */

namespace J\MVC;


use J\Application;
use J\Schema\CollectionManager;
use J\Schema\Syntax;
use J\Configurable;
use J\Exception;
use J\IConfigurable;
use J\Independent;
use J\Intent;
use J\Manager;
use J\Message;

/**
 * 模块类
 *   通过静态工厂生成实体模块对象
 *   通过 invoke 方法进入意图处理流程，意图经 模块 到 控制器（控制器启用流水线）处理
 *   控制器不共享,模型共享
 *
 */
class Module implements Independent, IConfigurable
{
    use Configurable;

    /**
     * @var Application
     */
    protected $app;

    /**
     * @var string $name
     */
    protected $name = null;

    /**
     * @var string 模块路径
     */
    protected $dir;

    /**
     * @var string 命名空间
     */
    protected $namespace;

    /**
     * @var Manager 模型管理器
     */
    protected $_models;

    /**
     * @var string 视图路径
     */
    protected $vir;

    /**
     * Constructor.
     * @param Application $app
     *
     */
    public function __construct($app)
    {
        $this->app = $app;

        if ($this->dir === null) {
            try {
                $class = new \ReflectionClass($this);
                $this->dir = dirname($class->getFileName());
            } catch (\Throwable $e)
            {

            }
        }

        if ($this->namespace === null) {
            $this->namespace = class_namespace($this);
        }

        // 语言注入
        $this->app->Language()->regDir($this->getDir());
        // 级联配置
        $file = $this->getDir() . DIRECTORY_SEPARATOR . 'config.php';
        $this->config = $this->createFileConfig($file, $app);
    }

    /**
     *  执行目标控制器
     *   通过抛出异常来申请返回错误页面
     * @param Intent $intent
     *
     * @return bool|null
     * @throws \Exception
     */
    public function invoke($intent)
    {
        // 本地处理器拦截
        if('install' === strtolower($intent->controller))
            return Install::install($this, $intent);

        return $this->getControl($intent->controller)->invoke($intent);
    }

    public function getName()
    {
        return $this->name;
    }

    public function getApp(): Application
    {
        return $this->app;
    }

    /**
     * @return string 配置文件、语言文件基本路径
     */
    public function getDir()
    {
        return $this->dir;
    }

    /**
     * 模块对象所处的基本命名空间
     *
     * @For Manager::getRealNamespace
     * @return string
     */
    public function getNamespace()
    {
        return $this->namespace;
    }

    /**
     * 中间接口:获取控制器
     * @param string $name
     * @return Controller
     * @throws \Exception
     */
    public function getControl($name)
    {
        // 默认控制器
        if(empty($name)) $name = $this->get('defaultController','Index');

        $obj = $this->create(
            $name,
            Syntax::KEY_CONTROLLERS,
            'controller',
            '\\J\\MVC\\Controller'
        );
        if($obj) return $obj;
        throw new Exception(
            Message::INVALID_CONTROLLER,
            ['module' => $this->name, 'controller' => $name]
        );
    }

    /**
     * 中间接口:获取模型管理器
     *
     * @return CollectionManager
     */
    public function getModels()
    {
        if(!$this->_models){
            $this->_models = new CollectionManager($this->get(Syntax::KEY_COLLECTIONS));
        }

        return $this->_models;
    }

    /**
     * 用于创建控制器或模型对象
     *
     * @param string $name 对象名
     * @param string $key controller/model
     * @param string $tail
     * @param string $default
     * @return Controller
     */
    public function create($name, $key, $tail, $default = null)
    {
        $system = $this->config->get($key, []);
        $config = array_isset($system,strtolower($name), []);

        $concrete = $this->prepareRealClassName($name, $tail);
        if(class_exists($concrete))
            return new $concrete($this, $name, $config);
        // $this->app->debug($concrete);
        // 必须在模块配置中定义
        if(isset($system[strtolower($name)]) and $default and class_exists($default))
            return new $default($this, $name, $config);
        // $this->app->debug($default);
        return null;
    }

    /**
     * 视图路径
     * @return string
     */
    public function getVir()
    {
        if ($this->vir !== null) {
            return $this->vir;
        } else {
            return $this->vir = $this->getDir() . DIRECTORY_SEPARATOR . 'Views';
        }
    }

    protected function prepareRealClassName($className,$tail)
    {
        $tail = ucfirst($tail);
        return $this->getNamespace().'\\'.$tail.'\\'.ucfirst($className).$tail;
    }

    /**
     * @param $collection
     * @param $rows
     * @throws Exception
     */
    public function insertValuesToCollection($collection, $rows)
    {
        $c = $this->getModels()->getCollection($collection);
        $curd = $c->getCurd();
        $id = [0];

        $key = $c->getTreeKey();


        foreach ($rows as $row) {
            $v = $row;

            if ($key) $v[$key] = $id[$v[$key]];

            $id[] = $curd->setElements($v)->add();
        }
    }

    /**
     * 系统内模块调起
     *
     * @param string|string[] $command
     * @param array $param
     * @return \J\ReturnValue
     */
    public function fetch($command, $param = [])
    {
        return $this->getApp()->getIntent()->fetch($command, $param);
    }
}